/**
 * Main program package.
 */
package fr.umlv.m2.ig.main;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeSelectionModel;

import org.xml.sax.SAXException;

import fr.umlv.m2.ig.handler.ConfigurationHandler;
import fr.umlv.m2.ig.handler.ConfigurationHandler.ConfigAction;
import fr.umlv.m2.ig.model.BeauheurModelImpl;
import fr.umlv.m2.ig.model.Line;
import fr.umlv.m2.ig.model.LinesTreeModel;
import fr.umlv.m2.ig.model.Station;
import fr.umlv.m2.ig.model.StationState;
import fr.umlv.m2.ig.model.StationType;
import fr.umlv.m2.ig.model.StationsTableModel;
import fr.umlv.m2.ig.model.StationsTableModel.DisplayWay;
import fr.umlv.m2.ig.model.Train;
import fr.umlv.m2.ig.model.event.SearchEvent;
import fr.umlv.m2.ig.model.event.SearchListener;
import fr.umlv.m2.ig.view.SubwayMapView;

/**
 * This class defines the main class of the program.
 * 
 * @author Gambouele Bertrand.
 * @author Viravout Alexandre.
 */
public class JacquesBeauheur {
  /**
   * The model of the application.
   */
  static BeauheurModelImpl model;

  /**
   * The map of the application.
   */
  static SubwayMapView mapView;
  
  /**
   * The current station line.
   */
  private static String line;
  /**
   * The current station.
   */
  private static String station;
  
  /**
   * JTable selected flag.
   */
  private static boolean tableSelected; 

  /**
   * Main function : entry point of the program.
   * @param args program arguments.
   */
  @SuppressWarnings("serial")
  public static void main(String[] args) {
	int refresh = 50;
    BeauheurModelImpl tmpModel = null;

    try {
      tmpModel = new BeauheurModelImpl(refresh);
      mapView = new SubwayMapView(tmpModel);
    } catch (SAXException e) {
      System.out.println("An error occured. Program will stop ...");
      e.printStackTrace();
      System.exit(1);
    } catch (IOException e) {
      System.out.println("An I/O error occured. Program will stop ...");
      e.printStackTrace();
      System.exit(1);
    }

    model = tmpModel;
    tableSelected = false;
    final JFrame frame = new JFrame("Beauheur");

    Timer timer = new Timer(refresh, new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent e) {
        model.moveTrains();
      }
    });

    timer.start();

    /**************************** PREFERENCES MANAGER *****************************/
    final JFrame preferencesFrame = new JFrame("Preferences");
    preferencesFrame.setSize(300, 350);

    /***** Configuration  part *****/
    final JButton loadFileButton = new JButton("Parcourir...");
    final JButton saveFileButton = new JButton("Sauvegarder...");

    final JFileChooser chooser = new JFileChooser();
    chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);

    final JPanel configPane  =  new JPanel();
    configPane.setBorder(BorderFactory.createTitledBorder("Configuration"));
    configPane.setLayout(new GridBagLayout());
    GridBagConstraints preferencesConfigGgbc = new GridBagConstraints();
    preferencesConfigGgbc.weightx = 1f;
    preferencesConfigGgbc.weighty = 1f;
    preferencesConfigGgbc.gridwidth = GridBagConstraints.REMAINDER;
    preferencesConfigGgbc.anchor = GridBagConstraints.WEST;
    Insets configInsets = new Insets(5, 10, 0, 0);
    preferencesConfigGgbc.insets = configInsets;
    configPane.add( new JLabel("Charger une configuration :"), preferencesConfigGgbc);
    configInsets.set(10, 20, 0, 0);
    configPane.add(loadFileButton, preferencesConfigGgbc);
    configInsets.set(30, 10, 0, 32);
    configPane.add(new JLabel("Sauvegarder une configuration :"), preferencesConfigGgbc);
    configInsets.set(10, 20, 10, 0);
    configPane.add(saveFileButton, preferencesConfigGgbc);

    final ConfigurationHandler config = new ConfigurationHandler(model);

    final ActionListener FileChooser = new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        int returnVal = chooser.showDialog(configPane, "Ouvrir");

        if(returnVal == JFileChooser.APPROVE_OPTION) {
          File file = chooser.getSelectedFile();
          config.setFile(file);

          if(e.getSource() == loadFileButton) {
            config.setAction(ConfigAction.LOAD);
          } else if(e.getSource() == saveFileButton) {
            config.setAction(ConfigAction.SAVE);
          }
        }
      }
    };

    loadFileButton.addActionListener(FileChooser);
    saveFileButton.addActionListener(FileChooser);

    /***** Settings part *****/
    final NumberFormat format = NumberFormat.getInstance();
    format.setMinimumFractionDigits(1);

    final SpinnerNumberModel trainSpeedModel = new SpinnerNumberModel(
        BeauheurModelImpl.TRAIN_SPEED,
        new Double(1.0),
        new Double(40.0),
        new Double(1.0)
    );
    final SpinnerNumberModel stationWaitingTimeModel = new SpinnerNumberModel(
        BeauheurModelImpl.TIME,
        new Double(0.0),
        new Double(20.0),
        new Double(1.0)
    );
    final JSpinner trainSpeed = new JSpinner(trainSpeedModel);
    final JSpinner stationWaitingTime = new JSpinner(stationWaitingTimeModel);
    
    trainSpeed.setPreferredSize(new Dimension(42, 23));
    stationWaitingTime.setPreferredSize(new Dimension(42, 23));

    final JPanel settingsPane = new JPanel();
    settingsPane.setBorder(BorderFactory.createTitledBorder("Paramétrage"));
    settingsPane.setLayout(new GridBagLayout());
    GridBagConstraints preferencesSettingsGbc = new GridBagConstraints();
    preferencesSettingsGbc.weightx = 1f;
    preferencesSettingsGbc.weighty = 1f;
    Insets settingsInsets = new Insets(5, 0, 0, 0);
    preferencesSettingsGbc.anchor = GridBagConstraints.EAST;
    preferencesSettingsGbc.gridwidth = GridBagConstraints.RELATIVE;
    preferencesSettingsGbc.insets = settingsInsets;
    settingsPane.add(new JLabel("Vitesse des trains :"), preferencesSettingsGbc);
    settingsInsets.set(5, 10, 0, 10);
    preferencesSettingsGbc.anchor = GridBagConstraints.WEST;
    preferencesSettingsGbc.gridwidth = GridBagConstraints.REMAINDER;
    settingsPane.add(trainSpeed, preferencesSettingsGbc);
    settingsInsets.set(5, 10, 10, 0);
    preferencesSettingsGbc.anchor = GridBagConstraints.EAST;
    preferencesSettingsGbc.gridwidth = GridBagConstraints.RELATIVE;
    settingsPane.add(new JLabel("Temps d'arrêt aux stations :"), preferencesSettingsGbc);
    settingsInsets.set(5, 10, 10, 10);
    preferencesSettingsGbc.anchor = GridBagConstraints.WEST;
    preferencesSettingsGbc.gridwidth = GridBagConstraints.REMAINDER;
    settingsPane.add(stationWaitingTime, preferencesSettingsGbc);

    /***** Validate part *****/
    final JButton preferencesValidateButton = new JButton("OK");
    preferencesValidateButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        if(config.getAction() == ConfigAction.LOAD) {
          config.loadConfig();
        } else if(config.getAction() == ConfigAction.SAVE) {
          config.saveConfig();
        }

        Iterator<Entry<String, Line>> lines = model.getLines().entrySet().iterator();
        Iterator<Entry<String, Station>> stations = model.getStations().entrySet().iterator();
        
        Line line;
        while(lines.hasNext()) {
          line = lines.next().getValue();
          model.setTrainSpeed(line.getName(), trainSpeedModel.getNumber().doubleValue());
        }
        
        Station station;
        while(stations.hasNext()) {
          station = stations.next().getValue();
          for(String l : station.getLines()) {
            if(station.getType() == StationType.STATION) {
              model.setWaitingTime(station.getName(), l, stationWaitingTimeModel.getNumber().doubleValue());
            } else if(station.getType() == StationType.CURVE) {
              model.setWaitingTime(station.getName(), l, 0.0);
            }
          }
        }

        preferencesFrame.dispose();
        frame.setEnabled(true);
        frame.setVisible(true);
      }
    });

    final JButton preferencesCancelButton = new JButton("Annuler");
    preferencesCancelButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        preferencesFrame.dispose();
        frame.setEnabled(true);
        frame.setVisible(true);
      }
    });

    final JPanel validateButtonsPane = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 5));
    validateButtonsPane.add(preferencesValidateButton);
    validateButtonsPane.add(preferencesCancelButton);

    /***** Window part *****/
    preferencesFrame.setLayout(new GridBagLayout());
    GridBagConstraints preferencesFrameGbc = new GridBagConstraints();
    preferencesFrameGbc.weightx = 1f;
    preferencesFrameGbc.weighty = 1f;
    preferencesFrameGbc.gridwidth = GridBagConstraints.REMAINDER;
    preferencesFrameGbc.anchor = GridBagConstraints.CENTER;
    preferencesFrame.getContentPane().add(configPane, preferencesFrameGbc);
    preferencesFrame.getContentPane().add(settingsPane, preferencesFrameGbc);
    preferencesFrameGbc.anchor = GridBagConstraints.EAST;
    preferencesFrame.getContentPane().add(validateButtonsPane, preferencesFrameGbc);

    preferencesFrame.addWindowListener(new FunctionsFrameWindowAdapter(frame));
    preferencesFrame.setResizable(false);
    preferencesFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

    /********************************** MENU BAR **********************************/
    final JMenuItem preferencesItem = new JMenuItem("Preferences");
    preferencesItem.setMnemonic(KeyEvent.VK_P);
    preferencesItem.addActionListener(new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent e) {
        frame.setEnabled(false);
        preferencesFrame.setVisible(true);
      }
    });

    final JMenuItem exitItem = new JMenuItem("Exit");
    exitItem.setMnemonic(KeyEvent.VK_E);
    exitItem.addActionListener(new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent e) {
        frame.dispose();
      }
    });

    final JMenu fileMenu = new JMenu("File");
    fileMenu.setMnemonic(KeyEvent.VK_F);
    fileMenu.add(preferencesItem);
    fileMenu.addSeparator();
    fileMenu.add(exitItem);

    final JMenuItem about = new JMenuItem("A propos de Beauheur");
    about.setMnemonic(KeyEvent.VK_A);
    about.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        JOptionPane
        .showMessageDialog(
            frame,
            "Beauheur\nVersion: 1.0\n\nDeveloped by :\n  Gambouele Bertrand\n  Viravout Alexandre",
            "A propos de Beauheur", JOptionPane.INFORMATION_MESSAGE);
      }
    });
    final JMenu helpMenu = new JMenu("?");
    helpMenu.setMnemonic('?');
    helpMenu.add(about);

    final JMenuBar menuBar = new JMenuBar();
    menuBar.add(fileMenu);
    menuBar.add(helpMenu);


    /************************************ MAP *************************************/
    final JLayeredPane mapPane = mapView.getPanel();

    /********************************** SETTINGS **********************************/
    /** Settings station part **/
    final JFrame settingsStationFrame = new JFrame();
    settingsStationFrame.setSize(250, 200);

    final JRadioButton stationOpen = new JRadioButton("Ouvert");
    stationOpen.setSelected(true);
    final JRadioButton stationClose = new JRadioButton("Fermé");    
    final ButtonGroup settingsStationState = new ButtonGroup();
    settingsStationState.add(stationOpen);
    settingsStationState.add(stationClose);

    final JButton settingsStationValidate = new JButton("Valider");

    final JButton settingsStationCancel = new JButton("Annuler");
    settingsStationCancel.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        settingsStationFrame.dispose();
        frame.setEnabled(true);
        frame.setVisible(true);
      }
    });

    final JPanel settingsStationValidatePane = new JPanel();
    settingsStationValidatePane.setLayout(new FlowLayout(FlowLayout.RIGHT, 7, 2));
    settingsStationValidatePane.add(settingsStationValidate);
    settingsStationValidatePane.add(settingsStationCancel);

    final JSpinner settingsStationWaitingTime = new JSpinner(stationWaitingTimeModel);
    settingsStationWaitingTime.setPreferredSize(new Dimension(42, 23));

    final JPanel settingsStationPart = new JPanel();
    settingsStationPart.setBorder(BorderFactory.createTitledBorder("Paramétrage"));
    settingsStationPart.setLayout(new GridBagLayout());
    GridBagConstraints settingsStationGbc = new GridBagConstraints();
    settingsStationGbc.weightx = 1f;
    settingsStationGbc.weighty = 1f;
    Insets settingsStationInsets = new Insets(10, 20, 10, 0);
    settingsStationGbc.insets = settingsStationInsets;
    settingsStationGbc.anchor = GridBagConstraints.WEST;
    settingsStationGbc.gridwidth = GridBagConstraints.RELATIVE;
    settingsStationPart.add(stationOpen, settingsStationGbc);
    settingsStationGbc.insets.set(10, 0, 10, 26);
    settingsStationGbc.gridwidth = GridBagConstraints.REMAINDER;
    settingsStationPart.add(stationClose, settingsStationGbc);
    settingsStationGbc.insets.set(10, 25, 15, 0);
    settingsStationGbc.gridwidth = GridBagConstraints.RELATIVE;
    settingsStationPart.add(new JLabel("Temps d'arrêt :"), settingsStationGbc);
    settingsStationGbc.insets.set(10, 15, 15, 0);
    settingsStationGbc.gridwidth = GridBagConstraints.REMAINDER;
    settingsStationPart.add(settingsStationWaitingTime, settingsStationGbc);

    final JPanel settingsStationPane = new JPanel();
    settingsStationPane.setLayout(new GridBagLayout());
    GridBagConstraints settingsStationPaneGbc = new GridBagConstraints();
    settingsStationPaneGbc.weightx = 1f;
    settingsStationPaneGbc.weighty = 1f;
    Insets settingsStationPaneInsets = new Insets(0, 5, 0, 5);
    settingsStationPaneGbc.insets = settingsStationPaneInsets;
    settingsStationPaneGbc.anchor = GridBagConstraints.WEST;
    settingsStationPaneGbc.gridwidth = GridBagConstraints.REMAINDER;
    settingsStationPane.add(settingsStationPart, settingsStationPaneGbc);
    settingsStationPaneGbc.insets.set(0, 0, 0, 0);
    settingsStationPaneGbc.anchor = GridBagConstraints.EAST;
    settingsStationPane.add(settingsStationValidatePane, settingsStationPaneGbc);

    settingsStationFrame.addWindowListener(new FunctionsFrameWindowAdapter(frame));
    settingsStationFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    settingsStationFrame.setResizable(false);
    settingsStationFrame.setContentPane(settingsStationPane);
    settingsStationFrame.setVisible(false);

    /** Settings stations part **/
    Map<String, Line> lines = model.getLines();
    final String[] railNetworkLines = new String[17];
    railNetworkLines[0] = "Sélectionner";
    int j = 0;
    for(int i=1; i<16; i++) {
      if(i > 14) {
        railNetworkLines[4] = "Ligne " + lines.get("3bis").getName();
        railNetworkLines[9] = "Ligne " + lines.get("7bis").getName();
      } else {
        railNetworkLines[(i+j)] = "Ligne " + lines.get(String.valueOf(i)).getName();

        if((i == 3) || (i == 7)) {
          j++;
        }
      }
    }

    final JComboBox settingsStationsList = new JComboBox(railNetworkLines);    
    final JTextField settingsStationsSearch = new JTextField();
    settingsStationsSearch.setPreferredSize(new Dimension(236, 25));

    final StationsTableModel tableModel = new StationsTableModel(model);
    
    final JTable settingsStationstable = new JTable(tableModel);
    settingsStationstable.setFillsViewportHeight(true);
    settingsStationstable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

    settingsStationstable.getColumnModel().getColumn(0).setPreferredWidth(45);
    settingsStationstable.getColumnModel().getColumn(1).setPreferredWidth(272);
    
    final JScrollPane settingsStationsScrollPane = new JScrollPane(settingsStationstable);
    settingsStationsScrollPane.setPreferredSize(new Dimension(320, 440));
    settingsStationsScrollPane.setAutoscrolls(true);
    
    settingsStationstable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
      @Override
      public Component getTableCellRendererComponent(
          JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column
      ) {
        Component component = super.getTableCellRendererComponent(
            table, value, isSelected, hasFocus, row, column
        );

        String name = (String) value;
        component.setFont(new Font("sanssherif", Font.BOLD, 11));
        
        Map<String, Color> colors = mapView.getLinesColor();
        if(column == 0) {
          component.setBackground(colors.get(name));
          component.setForeground(Color.WHITE);
          
          setHorizontalAlignment(SwingConstants.CENTER);
        } else if(column == 1) {
          String line = (String) tableModel.getValueAt(row, 0);
          Station station = model.getStation(name);
          
          if(station.getState(line) == StationState.CLOSED) {
            component.setForeground(new Color(255, 0, 0));
          } else {
            component.setForeground(Color.BLACK);
          }
          component.setBackground(Color.WHITE);

          setHorizontalAlignment(SwingConstants.LEFT);
        }
        
        return component;
      }
    });
    
    model.addSearchByLineListener(new SearchListener() {

      @Override
      public void updateSearch(SearchEvent event) {
        if(event.getDisplayStationsWay() == DisplayWay.LINE) {
          tableModel.setLine(event.getName());
        } else {
          tableModel.initFilteredList(event.getName());
        }

        settingsStationsScrollPane.repaint();
        settingsStationsScrollPane.revalidate();
      }
    });

    final JButton settingsStationsButton = new JButton("Paramétrer");
    
    settingsStationValidate.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        Station station = model.getStation(JacquesBeauheur.station);
        
        if(stationOpen.isSelected()) {
          if(station.getState(JacquesBeauheur.line) == StationState.CLOSED) {
            model.changeStationState(JacquesBeauheur.station, JacquesBeauheur.line);
          }
        } else if(station.getState(JacquesBeauheur.line) == StationState.OPENED) {
          model.changeStationState(JacquesBeauheur.station, JacquesBeauheur.line);
        }
        
        Double time = (Double)stationWaitingTime.getValue();
        model.setWaitingTime(JacquesBeauheur.station, JacquesBeauheur.line, time);
        
        settingsStationFrame.dispose();
        frame.setEnabled(true);
        frame.setVisible(true);
        
        settingsStationsScrollPane.repaint();
        settingsStationsScrollPane.revalidate();
      }
    });
    
    settingsStationsButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        frame.setEnabled(false);
        
        if(model.getStation(station).getState(line) == StationState.OPENED) {
          stationOpen.setSelected(true);
          stationClose.setSelected(false);
        } else {
          stationOpen.setSelected(false);
          stationClose.setSelected(true);
        }
        
        Double time = model.getWaitingTime(station, line);
        stationWaitingTimeModel.setValue(time);
        
        settingsStationFrame.setVisible(true);
      }
    });
    settingsStationsButton.setEnabled(false);

    settingsStationstable.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseClicked(MouseEvent e) {
        super.mouseClicked(e);
        JTable table = (JTable) e.getSource();
        
        tableSelected = true;
        updateSettingsStationFrame(tableModel, table, settingsStationFrame, settingsStationsButton);

        settingsStationsButton.setEnabled(true);
      }
    });

    settingsStationsList.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        JComboBox lines = (JComboBox) e.getSource();
        String lineLabel = (String) lines.getSelectedItem();

        String line;
        if(lineLabel == "Sélectionner") {
          line = "";
        } else {
          settingsStationsSearch.setText("");
          line = lineLabel.substring(lineLabel.indexOf(" ") + 1);
        }
        model.searchDisplayStationsByLine(line);
        
        if(tableModel.getRowCount() < settingsStationstable.getSelectedRow()) {
          tableSelected = false;
        } else if(!tableSelected && (settingsStationstable.getSelectedRow() != -1)) {
          settingsStationstable.removeRowSelectionInterval(
              settingsStationstable.getSelectedRow(), settingsStationstable.getSelectedRow()
          );
        }
        
        updateSettingsStationFrame(
            tableModel, settingsStationstable, settingsStationFrame, settingsStationsButton
        );
      }
    });

    settingsStationsSearch.addKeyListener(new KeyListener() {
      @Override
      public void keyTyped(KeyEvent e) {}

      @Override
      public void keyReleased(KeyEvent e) {
        settingsStationsList.setSelectedIndex(0);
        
        JTextField nameField = (JTextField) e.getSource();
        String name = nameField.getText();
        model.searchDisplayStationsByName(name);
        
        if(tableModel.getRowCount() < settingsStationstable.getSelectedRow()) {
          tableSelected = false;
        } else if(!tableSelected && (settingsStationstable.getSelectedRow() != -1)) {
          settingsStationstable.removeRowSelectionInterval(
              settingsStationstable.getSelectedRow(), settingsStationstable.getSelectedRow()
          );
        }
        
        updateSettingsStationFrame(
            tableModel, settingsStationstable, settingsStationFrame, settingsStationsButton
        );
      }

      @Override
      public void keyPressed(KeyEvent e) {}
    });
    
    final JPanel settingsStationsPane = new JPanel();
    settingsStationsPane.setLayout(new GridBagLayout());
    GridBagConstraints settingsStationsPaneGbc = new GridBagConstraints();
    settingsStationsPaneGbc.weightx = 1f;
    settingsStationsPaneGbc.weighty = 0f;
    Insets settingsStationsPaneInsets = new Insets(0, 20, 0, 0);
    settingsStationsPaneGbc.insets = settingsStationsPaneInsets;
    settingsStationsPaneGbc.anchor = GridBagConstraints.WEST;
    settingsStationsPaneGbc.gridwidth = GridBagConstraints.REMAINDER;
    settingsStationsPane.add(new JLabel("Rechercher une station par ligne :"), settingsStationsPaneGbc);
    settingsStationsPaneGbc.insets.set(5, 20, 0, 0);
    settingsStationsPane.add(settingsStationsList, settingsStationsPaneGbc);
    settingsStationsPaneGbc.insets.set(15, 20, 0, 0);
    settingsStationsPane.add(new JLabel("Rechercher une station par nom :"), settingsStationsPaneGbc);
    settingsStationsPaneGbc.insets.set(5, 20, 0, 0);
    settingsStationsPane.add(settingsStationsSearch, settingsStationsPaneGbc);
    settingsStationsPaneGbc.insets.set(10, 13, 0, 0);
    settingsStationsPane.add(settingsStationsScrollPane, settingsStationsPaneGbc);
    settingsStationsPaneGbc.insets.set(12, 0, 0, 13);
    settingsStationsPaneGbc.anchor = GridBagConstraints.EAST;
    settingsStationsPane.add(settingsStationsButton, settingsStationsPaneGbc);

    /** Settings line part **/
    final JComboBox settingsLineList = new JComboBox(railNetworkLines);
    final JPanel settingsLineListPart = new JPanel();
    settingsLineListPart.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 0));
    settingsLineListPart.add(new JLabel("Ligne :"));
    settingsLineListPart.add(settingsLineList);
    
    final JRadioButton lineOpen = new JRadioButton("Tout ouvrir");
    final JRadioButton lineClose = new JRadioButton("Tout fermer");
    final JRadioButton lineNone = new JRadioButton("Ne rien faire");
    lineNone.setSelected(true);
    final ButtonGroup settingsLineState = new ButtonGroup();
    settingsLineState.add(lineOpen);
    settingsLineState.add(lineClose);
    settingsLineState.add(lineNone);
    
    final JPanel settingsLineRadio = new JPanel();
    settingsLineRadio.setLayout(new FlowLayout());
    settingsLineRadio.add(lineOpen);
    settingsLineRadio.add(lineClose);
    settingsLineRadio.add(lineNone);

    final JButton settingsLineValidate = new JButton("Valider");
    final JButton settingsLineCancel = new JButton("Annuler");

    final JPanel settingsLineValidatePane = new JPanel();
    settingsLineValidatePane.setLayout(new FlowLayout(FlowLayout.RIGHT, 7, 7));
    settingsLineValidatePane.add(settingsLineValidate);
    settingsLineValidatePane.add(settingsLineCancel);

    final JSpinner settingsLineTrainSpeed = new JSpinner(trainSpeedModel);
    settingsLineTrainSpeed.setPreferredSize(new Dimension(42, 23));
    
    final JPanel settingsLineTrainSpeedPart = new JPanel();
    settingsLineTrainSpeedPart.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 3));
    settingsLineTrainSpeedPart.add(new JLabel("Vitesse des train :"));
    settingsLineTrainSpeedPart.add(settingsLineTrainSpeed);

    final JPanel settingsLinePart = new JPanel();
    settingsLinePart.setBorder(BorderFactory.createTitledBorder("Paramétrage"));
    settingsLinePart.setLayout(new GridBagLayout());
    GridBagConstraints settingsLineGbc = new GridBagConstraints();
    settingsLineGbc.weightx = 1f;
    settingsLineGbc.weighty = 1f;
    Insets settingsLineInsets = new Insets(5, 0, 0, 0);
    settingsLineGbc.insets = settingsLineInsets;
    settingsLineGbc.gridwidth = GridBagConstraints.WEST;
    settingsLineGbc.gridwidth = GridBagConstraints.REMAINDER;
    settingsLinePart.add(settingsLineListPart, settingsLineGbc);
    settingsLineGbc.insets.set(5, 0, 0, 0);
    settingsLineGbc.gridwidth = GridBagConstraints.REMAINDER;
    settingsLinePart.add(settingsLineRadio, settingsLineGbc);
    settingsLineGbc.insets.set(2, 0, 15, 0);
    settingsLineGbc.gridwidth = GridBagConstraints.REMAINDER;
    settingsLinePart.add(settingsLineTrainSpeedPart, settingsLineGbc);

    final JPanel settingsLinePane = new JPanel();
    settingsLinePane.setLayout(new GridBagLayout());
    GridBagConstraints settingsLinePaneGbc = new GridBagConstraints();
    settingsLinePaneGbc.weightx = 1f;
    settingsLinePaneGbc.weighty = 1f;
    Insets settingsLinePaneInsets = new Insets(0, 5, 0, 5);
    settingsLinePaneGbc.insets = settingsLinePaneInsets;
    settingsLinePaneGbc.anchor = GridBagConstraints.WEST;
    settingsLinePaneGbc.gridwidth = GridBagConstraints.REMAINDER;
    settingsLinePane.add(settingsLinePart, settingsLinePaneGbc);
    settingsLinePaneGbc.insets.set(0, 0, 0, 0);
    settingsLinePaneGbc.anchor = GridBagConstraints.EAST;
    settingsLinePane.add(settingsLineValidatePane, settingsLinePaneGbc);

    final JFrame settingsLineFrame = new JFrame("Paramétrage de ligne");
    settingsLineFrame.addWindowListener(new FunctionsFrameWindowAdapter(frame));
    settingsLineFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    settingsLineFrame.setSize(360, 210);
    settingsLineFrame.setResizable(false);
    settingsLineFrame.setContentPane(settingsLinePane);
    settingsLineFrame.setVisible(false);
    
    settingsLineCancel.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        settingsLineFrame.dispose();
        frame.setEnabled(true);
      }
    });

    settingsLineList.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        JComboBox comboBox = (JComboBox) e.getSource();
        
        if(comboBox.getSelectedItem() != "Sélectionner") {
          String lineLabel = (String)comboBox.getSelectedItem();
          String lineName = lineLabel.substring(lineLabel.indexOf(" ") + 1);
          
          trainSpeedModel.setValue(model.getTrainSpeed(lineName));
        }
      }
    });
    
    settingsLineValidate.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        String lineLabel = (String)settingsLineList.getSelectedItem();
        
        if(lineLabel != "Sélectionner") {
          String lineName = lineLabel.substring(lineLabel.indexOf(" ") + 1);
          
          Iterator<Entry<String, Station>> stations = model.getStations().entrySet().iterator();
          Station station;
          
          while(stations.hasNext()) {
            station = stations.next().getValue();
            
            if(lineOpen.isSelected()) {  
              if(station.getState(lineName) == StationState.CLOSED) {
                model.changeStationState(station.getName(), lineName);
              }
            } else if(lineClose.isSelected()) {
              if(station.getState(lineName) == StationState.OPENED) {
                model.changeStationState(station.getName(), lineName);
              }
            }
          }
          
          model.setTrainSpeed(lineName, (Double)trainSpeedModel.getValue());
        }
        
        settingsLineFrame.dispose();
        frame.setEnabled(true);
        frame.setVisible(true);
      }
    });
    
    /** Settings lines part **/    
    final JComboBox addTrainLinesList = new JComboBox(railNetworkLines);

    final JPanel addTrainContentPane = new JPanel();
    addTrainContentPane.setLayout(new GridBagLayout());
    addTrainContentPane.setBorder(BorderFactory.createTitledBorder("Train"));
    GridBagConstraints addTrainContentGbc = new GridBagConstraints();
    addTrainContentGbc.weightx = 1f;
    addTrainContentGbc.weighty = 1f;
    addTrainContentGbc.gridwidth = GridBagConstraints.RELATIVE;
    addTrainContentPane.add(new JLabel("Ligne :"), addTrainContentGbc);
    addTrainContentGbc.gridwidth = GridBagConstraints.REMAINDER;
    addTrainContentPane.add(addTrainLinesList, addTrainContentGbc);
    
    final JButton addTrainValidateButton = new JButton("Ajouter");
    final JButton addTrainCancelButton = new JButton("Fermer");
    
    final JPanel addTrainValidatePane = new JPanel();
    addTrainValidatePane.setLayout(new FlowLayout(FlowLayout.RIGHT, 5, 5));
    addTrainValidatePane.add(addTrainValidateButton);
    addTrainValidatePane.add(addTrainCancelButton);
    
    final JFrame addTrainFrame = new JFrame("Ajout de rame");
    addTrainFrame.setLayout(new BorderLayout());
    addTrainFrame.add(addTrainContentPane, BorderLayout.CENTER);
    addTrainFrame.add(addTrainValidatePane, BorderLayout.SOUTH);
    
    addTrainFrame.setSize(240, 140);
    addTrainFrame.addWindowListener(new FunctionsFrameWindowAdapter(frame));
    addTrainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    
    addTrainValidateButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        String lineLabel = (String)addTrainLinesList.getSelectedItem();
        
        if(lineLabel != "Sélectionner") {
          String lineName = lineLabel.substring(lineLabel.indexOf(" ") + 1);
          
          Map<String, Line> lines = model.getLines();
          Line line = lines.get(lineName);
          
          String trainID = (line.getName().length() > 2) ? 
              line.getName().substring(0, line.getName().indexOf("b")) + "B" : 
              line.getName() + "S";          
          
          Random rand = new Random();              
          String journey1 = line.getJourneys().get(rand.nextInt(line.getJourneys().size())).getOrigin();
          String journey2 = line.getJourneys().get(rand.nextInt(line.getJourneys().size())).getDestination();
          
          Train train = new Train(
              trainID+System.nanoTime(),
              line.getName(),
              journey1,
              journey2,
              journey1
          );
          
          model.addTrain(train);
          
          addTrainLinesList.setSelectedIndex(0);
        }
      }
    });
    
    addTrainCancelButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        addTrainFrame.dispose();
        frame.setEnabled(true);
      }
    });
    
    final TreeModel linesTree = new LinesTreeModel(model);
    final JTree settingsLinesTree = new JTree(linesTree);
    settingsLinesTree.setRootVisible(true);
    settingsLinesTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
    
    final JScrollPane settingsLinesScrollPane = new JScrollPane(settingsLinesTree);
    settingsLinesScrollPane.setPreferredSize(new Dimension(305, 540));

    final JButton settingsLineButton = new JButton("Paramétrer");
    settingsLineButton.setVisible(true);
    
    final JButton addTrainButton = new JButton("Ajouter une rame");
    addTrainButton.setVisible(true);
    
    final JButton settingsTrainButton = new JButton("Supprimer");
    settingsTrainButton.setVisible(false);
    
    
    settingsTrainButton.addActionListener(new ActionListener() {

      @Override
      public void actionPerformed(ActionEvent e) {

      }
    });

    final JPanel settingsTrainsValidationPane = new JPanel();
    settingsTrainsValidationPane.add(settingsLineButton);
    settingsTrainsValidationPane.add(addTrainButton);
    settingsTrainsValidationPane.add(settingsTrainButton);

    final JPanel settingsTrainsPane = new JPanel();
    settingsTrainsPane.setLayout(new GridBagLayout());
    GridBagConstraints settingsTrainsGbc = new GridBagConstraints();
    settingsTrainsGbc.weightx = 1f;
    settingsTrainsGbc.weighty = 0f;
    Insets settingsTrainsInsets = new Insets(10, 20, 0, 0);
    settingsTrainsGbc.insets = settingsTrainsInsets;
    settingsTrainsGbc.gridwidth = GridBagConstraints.REMAINDER;
    settingsTrainsGbc.anchor = GridBagConstraints.WEST;
    settingsTrainsPane.add(settingsLinesScrollPane, settingsTrainsGbc);
    settingsTrainsGbc.insets.set(10, 15, 0, 0);
    settingsTrainsPane.add(settingsTrainsValidationPane, settingsTrainsGbc);
    
    settingsLineButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        settingsLineList.setSelectedIndex(0);
        lineNone.setSelected(true);
        trainSpeedModel.setValue(BeauheurModelImpl.TRAIN_SPEED);
        
        frame.setEnabled(false);
        settingsLineFrame.setVisible(true);
      }
    });
    
    addTrainButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        frame.setEnabled(false);
        addTrainFrame.setVisible(true);
      }
    });
    
    settingsTrainButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        
      }
    });
    
    final JTabbedPane settingsTabbled = new JTabbedPane();
    settingsTabbled.setPreferredSize(new Dimension(350, 645));
    settingsTabbled.addTab("Les stations", settingsStationsPane);
    settingsTabbled.addTab("Les lignes", settingsTrainsPane);

    frame.setJMenuBar(menuBar);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().setLayout(new BorderLayout());

    frame.getContentPane().add(mapPane, BorderLayout.CENTER);
    frame.getContentPane().add(settingsTabbled, BorderLayout.EAST);
    frame.pack();
    frame.setResizable(false);
    frame.setVisible(true);

    frame.setEnabled(false);
    preferencesFrame.setVisible(true);
  }

  private static final void updateSettingsStationFrame(
      TableModel tableModel,
      JTable table,
      JFrame frame,
      JButton button
  ) {
    if(tableModel.getRowCount() > 0 && tableSelected) {
      button.setEnabled(true);

      String lineName = (String) tableModel.getValueAt(table.getSelectedRow(), 0);
      String stationName = (String) tableModel.getValueAt(table.getSelectedRow(), 1);

      frame.setTitle("Ligne "+lineName+" - "+stationName);
      
      line = lineName;
      station = stationName;
    } else {
      button.setEnabled(false);
    }
  }
}
